home *** CD-ROM | disk | FTP | other *** search
/ Graphics Plus / Graphics Plus.iso / msdos / fractal / frasr182 / calcmand.asm < prev    next >
Assembly Source File  |  1992-08-19  |  28KB  |  820 lines

  1. ;    CALCMAND.ASM - Mandelbrot/Julia Set calculation Routines
  2.  
  3. ;    This module runs as part of an overlay with calcfrac.c.
  4. ;    It must not be called from anywhere other than calcfrac.
  5.  
  6. ;    The routines in this code perform Mandelbrot and Julia set
  7. ;    calculations using 32-bit integer math as opposed to the
  8. ;    "traditional" floating-point approach.
  9.  
  10. ;    This code relies on several tricks to run as quickly as it does.
  11.  
  12. ;    One can fake floating point arithmetic by using integer
  13. ;    arithmetic and keeping track of the implied decimal point
  14. ;    if things are reasonable -- and in this case, they are.
  15. ;    I replaced code that looked like: z = x*y with code that
  16. ;    looks like:
  17. ;            ix = x * ifudge         (outside the loops)
  18. ;            iy = y * ifudge
  19. ;            ....
  20. ;            iz = (ix * iy) / ifudge     (inside the loops)
  21. ;    (and keep remembering that all the integers are "ifudged" bigger)
  22.  
  23. ;    The 386 has native 32-bit integer arithmetic, and (briefly) keeps
  24. ;    64-bit values around after 32-bit multiplies.    If the result is
  25. ;    divided down right away, you've got 64-bit arithmetic.   You just
  26. ;    have to ensure that the result after the divide is <= 32 bits long.
  27. ;    CPUs predating the 386 have to emulate 32-bit arithmetic using
  28. ;    16-bit arithmetic, which is significantly slower.
  29.  
  30. ;    Dividing is slow -- but shifting is fast, and we can select our
  31. ;    "fudge factor" to be a power of two, permitting us to use that
  32. ;    method instead.   In addition, the 386 can perform 32-bit wide
  33. ;    shifting -- and even 64-bit shifts with the following logic:
  34. ;            shdr    eax,edx,cl
  35. ;            shr    edx,cl
  36. ;    so we make sure that our "fudge factor" is a power of 2 and shift
  37. ;    it down that way.
  38. ;    Calcmand is hardcoded for a fudge factor of 2**29.
  39.  
  40.  
  41. ;                    Bert Tyler
  42. ; History since Fractint 16.0
  43. ;  (See comments with CJLT in them)
  44. ;  CJLT=Chris Lusby Taylor who has...
  45. ;
  46. ;   1. Speeded up 16 bit on 16 bit CPU
  47. ;    Minor changes, notably prescaling to fg14 before multiplying
  48. ;    instead of scaling the answer.
  49. ;    Also, I added overflow detection after adding linit, since it
  50. ;    seems this could overflow.  
  51. ;    Overall effect is about 10% faster on 386 with debugflag=8088
  52. ;   2. Speeded up 32 bit on 16 bit CPU
  53. ;    The macro `square' is totally rewritten, as is the logic for 2xy,
  54. ;    by prescaling x and y to fg31, not fg29. This allows us to do a
  55. ;    32 bit multiply in 3, not 4, 16 bit chunks while retaining full
  56. ;    fg29 accuracy.
  57. ;       Also, I removed lots of well-meaning but ineffective code handling
  58. ;    special cases of zeros and tidied up the handling of negative numbers,
  59. ;    so the routine is quite a bit shorter now and overall throughput of
  60. ;    Mandel is now over 40% faster on a 386 with debugflag=8088.
  61. ;       By the way, I was tempted to go the whole hog and replace x*x-y*y
  62. ;    by (x+y)*(x-y) to reduce 4 16-bit multiplys to 3, but it makes
  63. ;    escape detection a bit trickier. Another time, maybe.
  64. ;
  65.  
  66. ;             required for compatibility if Turbo ASM
  67. IFDEF ??version
  68. MASM51
  69. QUIRKS
  70. ENDIF
  71.  
  72. .MODEL    medium,c
  73. DGROUP          group   _DATA,_DATA2
  74.  
  75. .8086
  76.  
  77.     ; these must NOT be in any segment!!
  78.     ; this get's rid of TURBO-C fixup errors
  79.  
  80.     extrn    keypressed:far        ; this routine is in 'general.asm'
  81.     extrn    getakey:far        ; this routine is in 'general.asm'
  82.     extrn    iplot_orbit:far     ; this routine is in 'calcfrac.c'
  83.     extrn    scrub_orbit:far     ; this routine is in 'calcfrac.c'
  84.  
  85. _DATA2        segment DWORD PUBLIC 'DATA'
  86.  
  87. FUDGEFACTOR    equ    29        ; default (non-potential) fudgefactor
  88.  
  89. ; ************************ External variables *****************************
  90.  
  91.     extrn    fractype:word        ; == 0 if Mandelbrot set, else Julia
  92.     extrn    inside:word        ; "inside" color, normally 1 (blue)
  93.     extrn   outside:word            ; "outside" color, normally -1 (iter)
  94.     extrn    creal:dword, cimag:dword ; Julia Set Constant
  95.     extrn    delmin:dword        ; min increment - precision required
  96.     extrn    maxit:word        ; maximum iterations
  97.     extrn    lm:dword        ; magnitude bailout limit
  98.  
  99.     extrn    row:word, col:word    ; current pixel to calc
  100.     extrn    color:word        ; color calculated for the pixel
  101.     extrn    realcolor:word        ; color before inside,etc adjustments
  102.  
  103.     extrn    reset_periodicity:word    ; nonzero if to be reset
  104.     extrn    kbdcount:word        ; keyboard counter
  105.  
  106.     extrn    cpu:word        ; cpu type: 86, 186, 286, or 386
  107.     extrn    dotmode:word
  108.  
  109.     extrn    show_orbit:word     ; "show-orbit" flag
  110.     extrn    orbit_ptr:word        ; "orbit pointer" flag
  111.     extrn    periodicitycheck:word    ; no periodicity if zero
  112.  
  113.     public    linitx,linity        ; caller sets these
  114.     public    savedmask        ; caller sets this
  115.  
  116. ; ************************ Internal variables *****************************
  117.  
  118.         align    4
  119. x        dd    0        ; temp value: x
  120. y        dd    0        ; temp value: y
  121. absx        dd    0        ; temp value: abs(x)
  122. linitx        dd    0        ; initial value, set by calcfrac
  123. linity        dd    0        ; initial value, set by calcfrac
  124. savedmask    dd    0        ; saved values mask
  125. savedx        dd    0        ; saved values of X and Y iterations
  126. savedy        dd    0        ;  (for periodicity checks)
  127. k        dw    0        ; iteration countdown counter
  128. oldcolor    dw    0        ; prior pixel's escape time k value
  129. savedand    dw    0        ; AND value for periodicity checks
  130. savedincr    dw    0        ; flag for incrementing AND value
  131. period        db    0        ; periodicity, if in the lake
  132.  
  133. _DATA2        ends
  134.  
  135. .CODE
  136.  
  137. ; ***************** Function calcmandasm() **********************************
  138.  
  139.     public    calcmandasm
  140.  
  141. FRAME    MACRO regs
  142.     push    bp
  143.     mov    bp, sp
  144.     IRP    reg, <regs>
  145.       push    reg
  146.       ENDM
  147.     ENDM
  148.  
  149. UNFRAME MACRO regs
  150.     IRP    reg, <regs>
  151.       pop reg
  152.       ENDM
  153.     pop bp
  154.     ENDM
  155.  
  156. calcmandasm    proc
  157.     FRAME    <di,si>         ; std frame, for TC++ overlays
  158.     sub    ax,ax            ; clear ax
  159.     cmp    periodicitycheck,ax    ; periodicity checking disabled?
  160.     je    initoldcolor        ;  yup, set oldcolor 0 to disable it
  161.     cmp    reset_periodicity,ax    ; periodicity reset?
  162.     je    short initparms     ; inherit oldcolor from prior invocation
  163.     mov    ax,maxit        ; yup.    reset oldcolor to maxit-250
  164.     sub    ax,250            ; (avoids slowness at high maxits)
  165. initoldcolor:
  166.     mov    oldcolor,ax        ; reset oldcolor
  167.  
  168. initparms:
  169.     mov    ax,word ptr creal    ; initialize x == creal
  170.     mov    dx,word ptr creal+2    ;  ...
  171.     mov    word ptr x,ax        ;  ...
  172.     mov    word ptr x+2,dx     ;  ...
  173.  
  174.     mov    ax,word ptr cimag    ; initialize y == cimag
  175.     mov    dx,word ptr cimag+2    ;  ...
  176.     mov    word ptr y,ax        ;  ...
  177.     mov    word ptr y+2,dx     ;  ...
  178.  
  179.     mov    ax,maxit        ; setup k = maxit
  180.     inc    ax            ; (+ 1)
  181.     mov    k,ax            ;  (decrementing to 0 is faster)
  182.  
  183.     cmp    fractype,1        ; julia or mandelbrot set?
  184.     je    short dojulia        ; julia set - go there
  185.  
  186. ;    (Tim wants this code changed so that, for the Mandelbrot,
  187. ;    Z(1) = (x + iy) + (a + ib).  Affects only "fudged" Mandelbrots.
  188. ;    (for the "normal" case, a = b = 0, and this works, too)
  189. ;    cmp    word ptr x,0        ; Mandelbrot shortcut:
  190. ;    jne    short doeither        ;  if creal = cimag = 0,
  191. ;    cmp    word ptr x+2,0        ; the first iteration can be emulated.
  192. ;    jne    short doeither        ;  ...
  193. ;    cmp    word ptr y,0        ;  ...
  194. ;    jne    short doeither        ;  ...
  195. ;    cmp    word ptr y+2,0        ;  ...
  196. ;    jne    short doeither        ;  ...
  197. ;    dec    k            ; we know the first iteration passed
  198. ;    mov    dx,word ptr linitx+2    ; copy x = linitx
  199. ;    mov    ax,word ptr linitx    ;  ...
  200. ;    mov    word ptr x+2,dx     ;  ...
  201. ;    mov    word ptr x,ax        ;  ...
  202. ;    mov    dx,word ptr linity+2    ; copy y = linity
  203. ;    mov    ax,word ptr linity    ;  ...
  204. ;    mov    word ptr y+2,dx     ;  ...
  205. ;    mov    word ptr y,ax        ;  ...
  206.  
  207.     dec    k            ; we know the first iteration passed
  208.     mov    dx,word ptr linitx+2    ; add x += linitx
  209.     mov    ax,word ptr linitx    ;  ...
  210.     add    word ptr x,ax        ;  ...
  211.     adc    word ptr x+2,dx     ;  ...
  212.     mov    dx,word ptr linity+2    ; add y += linity
  213.     mov    ax,word ptr linity    ;  ...
  214.     add    word ptr y,ax        ;  ...
  215.     adc    word ptr y+2,dx     ;  ...
  216.     jmp    short doeither        ; branch around the julia switch
  217.  
  218. dojulia:                ; Julia Set initialization
  219.                     ; "fudge" Mandelbrot start-up values
  220.     mov    ax,word ptr x        ; switch x with linitx
  221.     mov    dx,word ptr x+2     ;  ...
  222.     mov    bx,word ptr linitx    ;  ...
  223.     mov    cx,word ptr linitx+2    ;  ...
  224.     mov    word ptr x,bx        ;  ...
  225.     mov    word ptr x+2,cx     ;  ...
  226.     mov    word ptr linitx,ax    ;  ...
  227.     mov    word ptr linitx+2,dx    ;  ...
  228.  
  229.     mov    ax,word ptr y        ; switch y with linity
  230.     mov    dx,word ptr y+2     ;  ...
  231.     mov    bx,word ptr linity    ;  ...
  232.     mov    cx,word ptr linity+2    ;  ...
  233.     mov    word ptr y,bx        ;  ...
  234.     mov    word ptr y+2,cx     ;  ...
  235.     mov    word ptr linity,ax    ;  ...
  236.     mov    word ptr linity+2,dx    ;  ...
  237.  
  238. doeither:                ; common Mandelbrot, Julia set code
  239.     mov    period,0        ; claim periodicity of 1
  240.     mov    savedand,1        ; initial periodicity check
  241.     mov    savedincr,1        ;  flag for incrementing periodicity
  242.     mov    word ptr savedx+2,0ffffh; impossible value of "old" x
  243.     mov    word ptr savedy+2,0ffffh; impossible value of "old" y
  244.     mov    orbit_ptr,0        ; clear orbits
  245.  
  246.     dec    kbdcount        ; decrement the keyboard counter
  247.     jns    short nokey        ;  skip keyboard test if still positive
  248.     mov    kbdcount,10        ; stuff in a low kbd count
  249.     cmp    show_orbit,0        ; are we showing orbits?
  250.     jne    quickkbd        ;  yup.  leave it that way.
  251.     mov    kbdcount,5000        ; else, stuff an appropriate count val
  252.     cmp    cpu,386         ; ("appropriate" to the CPU)
  253.     je    short kbddiskadj    ;  ...
  254. ;;    cmp    word ptr delmin+2,1    ; is 16-bit math good enough?
  255.     cmp    word ptr delmin+2,8    ; is 16-bit math good enough?
  256.     ja    kbddiskadj        ;  yes. test less often
  257.     mov    kbdcount,500        ;  no.    test more often
  258. kbddiskadj:
  259.     cmp    dotmode,11        ; disk video?
  260.     jne    quickkbd        ;  no, leave as is
  261.     shr    kbdcount,1        ; yes, reduce count
  262.     shr    kbdcount,1        ;  ...
  263. quickkbd:
  264.     call    far ptr keypressed    ; has a key been pressed?
  265.     cmp    ax,0            ;  ...
  266.     je    nokey            ; nope.  proceed
  267.     mov    kbdcount,0        ; make sure it goes negative again
  268.     cmp    ax,'o'                  ; orbit toggle hit?
  269.     je    orbitkey        ;  yup.  show orbits
  270.     cmp    ax,'O'                  ; orbit toggle hit?
  271.     jne    keyhit            ;  nope.  normal key.
  272. orbitkey:
  273.     call    far ptr getakey     ; read the key for real
  274.     mov    ax,1            ; reset orbittoggle = 1 - orbittoggle
  275.     sub    ax,show_orbit        ;  ...
  276.     mov    show_orbit,ax        ;  ...
  277.     jmp    short nokey        ; pretend no key was hit
  278. keyhit: mov    ax,-1            ; return with -1
  279.     mov    color,ax        ; set color to -1
  280.     UNFRAME <si,di>         ; pop stack frame
  281.     ret                ; bail out!
  282.  
  283. nokey:
  284.     cmp    show_orbit,0        ; is orbiting on?
  285.     jne    no16bitcode        ;  yup.  slow down.
  286.     cmp    cpu,386         ; are we on a 386?
  287.     je    short code386bit    ;  YAY!! 386-class speed!
  288. ;;    cmp    word ptr delmin+2,1    ; OK, we're desperate.  16 bits OK?
  289.     cmp    word ptr delmin+2,8    ; OK, we're desperate.  16 bits OK?
  290.     ja    yes16bitcode        ;  YAY!  16-bit speed!
  291. no16bitcode:
  292.     call    near ptr code32bit    ; BOO!! nap time.  Full 32 bit math
  293.     jmp    kloopend        ;  bypass the 386-specific code.
  294. yes16bitcode:
  295.     call    near ptr code16bit    ; invoke the 16-bit version
  296.     jmp    kloopend        ;  bypass the 386-specific code.
  297.  
  298. .386                    ; 386-specific code starts here
  299.  
  300. code386bit:
  301. ;;    cmp    word ptr delmin+2,3    ; is 16-bit math good enough?
  302.     cmp    word ptr delmin+2,8    ; is 16-bit math good enough?
  303.     jbe    code386_32        ; nope, go do 32 bit stuff
  304. IFDEF ??version
  305.     jmp    code386_32        ; TASM screws up IMUL EBX,EBX!!
  306. ENDIF
  307.  
  308.     ; 16 bit on 386, now we are really gonna move
  309.     movsx    esi,word ptr x+2    ; use SI for X
  310.     movsx    edi,word ptr y+2    ; use DI for Y
  311.     push    ebp
  312.     mov    ebp,-1
  313.     shl    ebp,FUDGEFACTOR-1
  314.     mov    cx,FUDGEFACTOR-16
  315.  
  316. kloop386_16:   ; cx=bitshift-16, ebp=overflow.mask
  317.  
  318.     mov    ebx,esi         ; compute (x * x)
  319.     imul    ebx,ebx         ;  ...
  320.     test    ebx,ebp         ;
  321.     jnz    short end386_16     ;  (oops.  We done.)
  322.     shr    ebx,cl            ; get result down to 16 bits
  323.  
  324.     mov    edx,edi         ; compute (y * y)
  325.     imul    edx,edx         ;  ...
  326.     test    edx,ebp         ; say, did we overflow? <V20-compat>
  327.     jnz    short end386_16     ;  (oops.  We done.)
  328.     shr    edx,cl            ; get result down to 16 bits
  329.  
  330.     mov    ax,bx            ; compute (x*x - y*y) / fudge
  331.     sub    bx,dx            ;  for the next iteration
  332.  
  333.     add    ax,dx            ; compute (x*x + y*y) / fudge
  334.  
  335.     cmp    ax,word ptr lm+2    ; while (xx+yy < lm)
  336.     jae    short end386_16     ;  ...
  337.  
  338.     imul    edi,esi         ; compute (y * x)
  339.     shl    edi,1            ; ( * 2 / fudge)
  340.     sar    edi,cl
  341.     add    di,word ptr linity+2    ; (2*y*x) / fudge + linity
  342.     movsx    edi,di            ; save as y
  343.  
  344.     add    bx,word ptr linitx+2    ; (from above) (x*x - y*y)/fudge + linitx
  345.     movsx    esi,bx            ; save as x
  346.  
  347.     mov    ax,oldcolor        ; recall the old color
  348.     cmp    ax,k            ; check it against this iter
  349.     jge    short chkpd386_16    ;  yup.  do periodicity check.
  350. nonmax386_16:
  351.     dec    k            ; while (k < maxit)
  352.     jnz    short kloop386_16    ; try, try again
  353. end386_16:
  354.     pop    ebp
  355.     jmp    kloopend        ; we done
  356.  
  357. chkpd386_16:
  358.     mov    ax,k            ; set up to test for save-time
  359.     test    ax,savedand        ; save on 0, check on anything else
  360.     jz    short chksv386_16    ;  time to save a new "old" value
  361.     mov    ax,si            ; load up x
  362.     xor    ax,word ptr savedx+2    ; does X match?
  363.     test    ax,word ptr savedmask+2 ;  truncate to appropriate precision
  364.     jne    short nonmax386_16    ;  nope.  forget it.
  365.     mov    ax,di            ; now test y
  366.     xor    ax,word ptr savedy+2    ; does Y match?
  367.     test    ax,word ptr savedmask+2 ;  truncate to appropriate precision
  368.     jne    short nonmax386_16    ;  nope.  forget it.
  369.     mov    period,1        ; note that we have found periodicity
  370.     mov    k,0            ; pretend maxit reached
  371.     jmp    short end386_16
  372. chksv386_16:
  373.     mov    word ptr savedx+2,si    ; save x
  374.     mov    word ptr savedy+2,di    ; save y
  375.     dec    savedincr        ; time to change the periodicity?
  376.     jnz    short nonmax386_16    ;  nope.
  377.     shl    savedand,1        ; well then, let's try this one!
  378.     inc    savedand        ;  (2**n -1)
  379.     mov    savedincr,4        ; and reset the increment flag
  380.     jmp    short nonmax386_16
  381.  
  382.     ; 32bit on 386:
  383. code386_32:
  384.     mov    esi,x            ; use ESI for X
  385.     mov    edi,y            ; use EDI for Y
  386.  
  387. ;    This is the main processing loop.  Here, every T-state counts...
  388.  
  389. kloop:                    ; for (k = 0; k <= maxit; k++)
  390.  
  391.     mov    eax,esi         ; compute (x * x)
  392.     imul    esi            ;  ...
  393.     shrd    eax,edx,FUDGEFACTOR    ; ( / fudge)
  394.     shr    edx,FUDGEFACTOR-1    ; (complete 64-bit shift and check
  395.     jne    short kloopend1     ; bail out if too high
  396.     mov    ebx,eax         ; save this for below
  397.  
  398.     mov    eax,edi         ; compute (y * y)
  399.     imul    edi            ;  ...
  400.     shrd    eax,edx,FUDGEFACTOR    ; ( / fudge)
  401.     shr    edx,FUDGEFACTOR-1    ; (complete 64-bit shift and check
  402.     jne    short kloopend1     ; bail out if too high
  403.  
  404.     mov    ecx,ebx         ; compute (x*x - y*y) / fudge
  405.     sub    ebx,eax         ;  for the next iteration
  406.  
  407.     add    ecx,eax         ; compute (x*x + y*y) / fudge
  408.     cmp    ecx,lm            ; while (lr < lm)
  409.     jae    short kloopend1     ;  ...
  410.  
  411.     mov    eax,edi         ; compute (y * x)
  412.     imul    esi            ;  ...
  413.     shrd    eax,edx,FUDGEFACTOR-1    ;  ( * 2 / fudge)
  414.     add    eax,linity        ;  (above) + linity
  415.     mov    edi,eax         ;  save this as y
  416.  
  417. ;    (from the earlier code)     ; compute (x*x - y*y) / fudge
  418.     add    ebx,linitx        ;    + linitx
  419.     mov    esi,ebx         ; save this as x
  420.  
  421.     mov    ax,oldcolor        ; recall the old color
  422.     cmp    ax,k            ; check it against this iter
  423.     jge    short chkperiod1
  424. nonmax1:
  425.     dec    k            ; while (k < maxit) (dec to 0 is faster)
  426.     jnz    short kloop        ; while (k < maxit) ...
  427. kloopend1:
  428.     jmp    short kloopend32    ; we done.
  429.  
  430. chkperiod1:
  431.     mov    eax,esi
  432.     xor    eax,savedx
  433.     test    eax,savedmask
  434.     jnz    short chksave1
  435.     mov    eax,edi
  436.     xor    eax,savedy
  437.     test    eax,savedmask
  438.     jnz    short chksave1
  439.     mov    period,1        ; note that we have found periodicity
  440.     mov    k,0            ; pretend maxit reached
  441.     jmp    short kloopend1
  442. chksave1:
  443.     mov    ax,k
  444.     test    ax,savedand
  445.     jne    short nonmax1
  446.     mov    savedx,esi
  447.     mov    savedy,edi
  448.     dec    savedincr        ; time to change the periodicity?
  449.     jnz    short nonmax1        ;  nope.
  450.     shl    savedand,1        ; well then, let's try this one!
  451.     inc    savedand        ;  (2**n -1)
  452.     mov    savedincr,4        ; and reset the increment flag
  453.     jmp    short nonmax1
  454.  
  455. kloopend32:
  456.  
  457. .8086                    ; 386-specific code ends here
  458.  
  459. kloopend:
  460.     cmp    orbit_ptr,0        ; any orbits to clear?
  461.     je    noorbit2        ;  nope.
  462.     call    far ptr scrub_orbit    ; clear out any old orbits
  463. noorbit2:
  464.  
  465.     mov    ax,k            ; set old color
  466.     sub    ax,10            ; minus 10, for safety
  467.     mov    oldcolor,ax        ; and save it as the "old" color
  468.     mov    ax,maxit        ; compute color
  469.     sub    ax,k            ;  (first, re-compute "k")
  470.     sub    kbdcount,ax        ; adjust the keyboard count
  471.     cmp    ax,1            ; convert any "outlier" region
  472.     jge    short coloradjust1    ;  (where abs(x) > 2 or abs(y) > 2)
  473.     mov    ax,1            ;   to look like we ran through
  474. coloradjust1:                ;    at least one loop.
  475.     mov     realcolor,ax            ; result before adjustments
  476.     cmp     ax,maxit                ; did we max out on iterations?
  477.     jne     short notmax            ;  nope.
  478.     mov     oldcolor,ax             ; set "oldcolor" to maximum
  479.     cmp     inside,0                ; is "inside" >= 0?
  480.     jl      wedone                  ;  nope.  leave it at "maxit"
  481.     mov     ax,inside               ; reset max-out color to default
  482.     cmp     periodicitycheck,0      ; show periodicity matches?
  483.     jge     wedone                  ;  nope.
  484.     mov     al,period               ;  reset color to periodicity flag
  485.     jmp     short wedone
  486.  
  487. notmax:
  488.     cmp     outside,0               ; is "outside" >= 0?
  489.     jl      wedone                  ;   nope. leave as realcolor
  490.     mov     ax, outside             ; reset to "outside" color
  491.  
  492. wedone:                                 ;
  493.     mov     color,ax                ; save the color result
  494.     UNFRAME <si,di>                 ; pop stack frame
  495.     ret                             ; and return with color
  496.  
  497. calcmandasm endp
  498.  
  499.  
  500. ; ******************** Function code16bit() *****************************
  501. ;
  502. ;    Performs "short-cut" 16-bit math where we can get away with it.
  503. ; CJLT has modified it, mostly by preshifting x and y to fg30 from fg29
  504. ; or, since we ignore the lower 16 bits, fg14 from fg13.
  505. ; If this shift overflows we are outside x*x+y*y=2, so have escaped.
  506. ; Also, he commented out several conditional jumps which he calculated could
  507. ; never be taken (e.g. mov ax,si / imul si ;cannot overflow).
  508.  
  509. code16bit    proc    near
  510.  
  511.     mov    si,word ptr x+2     ; use SI for X fg13
  512.     mov    di,word ptr y+2     ; use DI for Y fg13
  513.  
  514. start16bit:
  515.     add    si,si            ;CJLT-Convert to fg14
  516.     jo    end16bit        ;overflows if <-2 or >2
  517.     mov    ax,si            ; compute (x * x)
  518.     imul    si            ; Answer is fg14+14-16=fg12
  519. ;    cmp    dx,0            ;CJLT commented out-
  520. ;    jl    end16bit        ;CJLT-  imul CANNOT overflow
  521. ;    mov    cx,32-FUDGEFACTOR    ;CJLT. FUDGEFACTOR=29 is hard coded
  522. loop16bit1:
  523.     shl    ax,1            ;  ...
  524.     rcl    dx,1            ;  ...
  525.     jo    end16bit        ;  (oops.  overflow)
  526. ;    loop    loop16bit1        ;CJLT...do it once only. dx now fg13.
  527.     mov    bx,dx            ; save this for a tad
  528.  
  529. ;ditto for y*y...
  530.  
  531.     add    di,di            ;CJLT-Convert to fg14
  532.     jo    end16bit        ;overflows if <-2 or >2
  533.     mov    ax,di            ; compute (y * y)
  534.     imul    di            ;  ...
  535. ;    cmp    dx,0            ; say, did we overflow? <V20-compat>
  536. ;    jl    end16bit        ;  (oops.  We done.)
  537. ;    mov    cx,32-FUDGEFACTOR    ; ( / fudge)
  538. ;loop16bit2:
  539.     shl    ax,1            ;  ...
  540.     rcl    dx,1            ;  ...
  541.     jo    end16bit        ;  (oops.  overflow)
  542. ;    loop    loop16bit2        ;  ...
  543.  
  544.     mov    cx,bx            ; compute (x*x - y*y) / fudge
  545.     sub    bx,dx            ;  for the next iteration
  546.  
  547.     add    cx,dx            ; compute (x*x + y*y) / fudge
  548.     jo    end16bit        ; bail out if too high
  549. ;    js    end16bit        ;  ...
  550.  
  551.     cmp    cx,word ptr lm+2    ; while (xx+yy < lm)
  552.     jae    end16bit        ;  ...
  553.     dec    k            ; while (k < maxit)
  554.     jz    end16bit        ;  we done.
  555.  
  556.     mov    ax,di            ; compute (y * x) fg14+14=fg28
  557.     imul    si            ;  ...
  558. ;    mov    cx,33-FUDGEFACTOR-2    ; ( * 2 / fudge)
  559. ;loop16bit3:
  560.     shl    ax,1            ;  ...
  561.     rcl    dx,1            ;  ...
  562.     shl    ax,1            ;  shift two bits
  563.     rcl    dx,1            ;  cannot overflow as |x|<=2, |y|<=2
  564. ;    loop    loop16bit3        ;  ...
  565.     add    dx,word ptr linity+2    ; (2*y*x) / fudge + linity
  566.     jo    end16bit        ; bail out if too high
  567.     mov    di,dx            ; save as y
  568.  
  569.     add    bx,word ptr linitx+2    ; (from above) (x*x - y*y)/fudge + linitx
  570.     jo    end16bit        ; bail out if too high
  571.     mov    si,bx            ; save as x
  572.  
  573.     mov    ax,oldcolor        ; recall the old color
  574.     cmp    ax,k            ; check it against this iter
  575.     jle    short nonmax3        ;  nope.  bypass periodicity check.
  576.     mov    word ptr x+2,si     ; save x for periodicity check
  577.     mov    word ptr y+2,di     ; save y for periodicity check
  578.     call    checkperiod        ; check for periodicity
  579. nonmax3:
  580.     jmp    start16bit        ; try, try again.
  581.  
  582. end16bit:                ; we done.
  583.     ret
  584. code16bit    endp
  585.  
  586.  
  587. ;    The following routine checks for periodic loops (a known
  588. ;    method of decay inside "Mandelbrot Lake", and an easy way to
  589. ;    bail out of "lake" points quickly).  For speed, only the
  590. ;    high-order sixteen bits of X and Y are checked for periodicity.
  591. ;    For accuracy, this routine is only fired up if the previous pixel
  592. ;    was in the lake (which means that the FIRST "wet" pixel was
  593. ;    detected by the dull-normal maximum iteration process).
  594.  
  595. checkperiod    proc near        ; periodicity check
  596.     mov    ax,k            ; set up to test for save-time
  597.     test    ax,savedand        ; save on 0, check on anything else
  598.     jz    checksave        ;  time to save a new "old" value
  599.     mov    dx,word ptr x+2     ; load up x
  600.     and    dx,word ptr savedmask+2 ;  truncate to appropriate precision
  601.     cmp    dx,word ptr savedx+2    ; does X match?
  602.     jne    checkdone        ;  nope.  forget it.
  603.     mov    ax,word ptr x        ; load up x
  604.     and    ax,word ptr savedmask    ;  truncate to appropriate precision
  605.     cmp    ax,word ptr savedx    ; does X match?
  606.     jne    checkdone        ;  nope.  forget it.
  607.     mov    dx,word ptr y+2     ; now test y
  608.     and    dx,word ptr savedmask+2 ;  truncate to appropriate precision
  609.     cmp    dx,word ptr savedy+2    ; does Y match?
  610.     jne    checkdone        ;  nope.  forget it.
  611.     mov    ax,word ptr y        ; load up y
  612.     and    ax,word ptr savedmask    ;  truncate to appropriate precision
  613.     cmp    ax,word ptr savedy    ; does Y match?
  614.     jne    checkdone        ;  nope.  forget it.
  615.     mov    period,1        ; note that we have found periodicity
  616.     mov    k,1            ; pretend maxit reached
  617. checksave:
  618.     mov    dx,word ptr x+2     ; load up x
  619.     and    dx,word ptr savedmask+2 ;  truncate to appropriate precision
  620.     mov    word ptr savedx+2,dx    ;  and save it
  621.     mov    ax,word ptr x        ; load up x
  622.     and    ax,word ptr savedmask    ;  truncate to appropriate precision
  623.     mov    word ptr savedx,ax    ;  and save it
  624.     mov    dx,word ptr y+2     ; load up y
  625.     and    dx,word ptr savedmask+2 ;  truncate to appropriate precision
  626.     mov    word ptr savedy+2,dx    ;  and save it
  627.     mov    ax,word ptr y        ; load up y
  628.     and    ax,word ptr savedmask    ;  truncate to appropriate precision
  629.     mov    word ptr savedy,ax    ;  and save it
  630.     dec    savedincr        ; time to change the periodicity?
  631.     jnz    checkdone        ;  nope.
  632.     shl    savedand,1        ; well then, let's try this one!
  633.     inc    savedand        ;  (2**n -1)
  634.     mov    savedincr,4        ; and reset the increment flag
  635. checkdone:
  636.     ret                ; we done.
  637. checkperiod    endp
  638.  
  639.  
  640. ; ******************** Function code32bit() *****************************
  641. ;
  642. ;    Perform the 32-bit logic required using 16-bit logic
  643. ;
  644. ;    New twice as fast logic,
  645. ;       Courtesy of Bill Townsend and Mike Gelvin (CIS:73337,520)
  646. ;    Even newer, faster still by Chris Lusby Taylor
  647. ;     who noted that we needn't square the low word if we first multiply
  648. ;     by 4, since we only need 29 places after the point and this will
  649. ;     give 30. (We divide answer by two to give 29 bit shift in answer)
  650. ;    Also, he removed all testing for special cases where a word of operand
  651. ;    happens to be 0, since testing 65536 times costs more than the saving
  652. ;    1 time out of 65536! (I benchmarked it. Just removing the tests speeds
  653. ;    us up by 3%.)
  654. ;
  655. ;Note that square returns DI,AX squared in DX,AX now.
  656. ; DI,AX is first converted to unsigned fg31 form.
  657. ; (For its square to be representable in fg29 (range -4..+3.999)
  658. ; DI:AX must be in the range 0..+1.999 which fits neatly into unsigned fg31.)
  659. ; This allows us to ignore the part of the answer corresponding to AX*AX as it
  660. ; is less than half a least significant bit of the final answer.
  661. ; I thought you'd like that.
  662. ;
  663. ; As we prescaled DI:AX, we need to shift the answer 1 bit to the right to
  664. ; end up in fg29 form since 29=(29+2)+(29+2)-32-1
  665. ; However, the mid term AX*DI is needed twice, so the shifts cancel.
  666. ;
  667. ; Since abs(x) and abs(y) in fg31 form will be needed in calculating 2*X*Y
  668. ; we push them onto the stack for later use.
  669.  
  670. ; Note that square does nor affect bl,si,bp
  671. ; and leaves highword of argument in di
  672. ; but destroys bh,cx
  673. square    MACRO    donepops
  674.     LOCAL    notneg
  675.     shl    ax,1        ;Multiply by 2 to convert to fg30
  676.     rcl    di,1        ;If this overflows DI:AX was negative
  677.     jnc    notneg
  678.     not    ax            ; so negate it
  679.     not    di            ; ...
  680.     add    ax,1            ; ...
  681.     adc    di,0            ; ...
  682.     not    bl            ; change negswt
  683. notneg:    shl    ax,1        ;Multiply by 2 again to give fg31
  684.     rcl    di,1        ;If this gives a carry then DI:AX was >=2.0
  685.                 ;If its high bit is set then DI:AX was >=1.0
  686.                 ;This is OK, but note that this means that
  687.                 ;DI:AX must now be treated as unsigned.
  688.     jc    donepops
  689.     push    di        ; save y or x (in fg31 form) on stack
  690.     push    ax        ; ...
  691.     mul    di        ;GET MIDDLE PART - 2*A*B
  692.     mov    bh,ah        ;Miraculously, it needs no shifting!
  693.     mov    cx,dx
  694.     mov    ax,di
  695.     mul    ax        ;SQUARE HIGH HWORD - A*A
  696.     shl    bh,1        ;See if we round up
  697.     adc    ax,1        ;Anyway, add 1 to round up/down accurately
  698.     adc    dx,0
  699.     shr    dx,1        ;This needs shifting one bit
  700.     rcr    ax,1
  701.     add    ax,cx        ;Add in the 2*A*B term
  702.     adc    dx,0
  703.     ENDM    ;#EM
  704.  
  705.  
  706. code32bit    proc near
  707. ;
  708. ; BL IS USED FOR THE "NEGSWT" FLAG
  709. ;   NEGSWT IS ONE IF EITHER "X" OR "Y" ARE NEGATIVE, BUT NOT BOTH NEGATIVE
  710. ;
  711.  
  712.     push    bp
  713.     xor    bl,bl            ; NEGSWT STARTS OFF ZERO
  714.  
  715. ;    iteration loop
  716.  
  717. nextit:    mov    ax,word ptr y        ; ax=low(y)
  718.     mov    di,word ptr y+2     ; di=high(y)
  719.     square done0    ;square y and quit via done0 if it overflows
  720.     mov    si,ax        ; square returns results in dx,ax
  721.     mov    bp,dx        ; save y*y in bp,si
  722.     mov    ax,word ptr x
  723.     mov    di,word ptr x+2
  724.     square    done2        ; square x and quit via done2 if it overflows
  725.     mov    cx,ax        ; Save low answer in cx.
  726.     ADD    ax,si        ; calc y*y + x*x
  727.     mov    ax,bp
  728.     ADC    ax,dx        ;  ...
  729.     jno    nextxy        ; overflow?
  730.                 ;NOTE: The original code tests against lm
  731.                 ;here, but as lm=4<<29 this is the same
  732.                 ;as testing for signed overflow
  733. done4:    add    sp,4            ; discard saved value of |x| fg 31
  734. done2:    add    sp,4            ; discard saved value of |y| fg 31
  735. done0: pop    bp            ; restore saved bp
  736.     ret
  737.  
  738. ;---------------------------------------------------------------------------
  739.  
  740. nextxy:    dec    k        ; while (k < maxit)
  741.     jz    done4        ;  we done.
  742.     sub    cx,si            ; subtract y*y from x*x
  743.     sbb    dx,bp            ;  ...
  744.     add    cx,word ptr linitx    ; add "A"
  745.     adc    dx,word ptr linitx+2    ;  ...
  746.     jo    done4            ;CJLT-Must detect overflow here
  747.                     ; but increment loop count first
  748.     mov    word ptr x,cx        ; store new x = x*x-y*y+a
  749.     mov    word ptr x+2,dx     ;  ...
  750.  
  751. ; now calculate x*y
  752. ;
  753. ;More CJLT tricks here. We use the pushed abs(x) and abs(y) in fg31 form
  754. ;which, when multiplied, give x*y in fg30, which, luckily, is the same as...
  755. ;2*x*y fg29.
  756. ;As with squaring, we can ignore the product of the low order words, and still
  757. ;be more accurate than the original algorithm.
  758. ;
  759.     pop    bp        ;Xlow
  760.     pop    di        ;Xhigh (already there, actually)
  761.     pop    ax        ;Ylow
  762.     mul    di        ;Xhigh * Ylow
  763.     mov    bh,ah        ;Discard lowest 8 bits of answer
  764.     mov    cx,dx
  765.     pop    ax        ;Yhigh
  766.     mov    si,ax        ; copy it
  767.     mul    bp        ;Xlow * Yhigh
  768.     xor    bp,bp        ;Clear answer
  769.     add    bh,ah
  770.     adc    cx,dx
  771.     adc    bp,0
  772.     mov    ax,si        ;Yhigh
  773.     mul    di        ;Xhigh * Yhigh
  774.     shl    bh,1        ;round up/down
  775.     adc    ax,cx        ;Answer-low
  776.     adc    dx,bp        ;Answer-high
  777.                 ;NOTE: The answer is 0..3.9999 in fg29
  778.     js    done0        ;Overflow if high bit set
  779.     or    bl,bl        ; ZERO IF NONE OR BOTH X , Y NEG
  780.     jz    signok        ; ONE IF ONLY ONE OF X OR Y IS NEG
  781.     not    ax        ; negate result
  782.     not    dx        ;  ...
  783.     add    ax,1        ;  ...
  784.     adc    dx,0        ;  ...
  785.     xor    bl,bl        ;Clear negswt
  786. signok:
  787.     add    ax,word ptr linity
  788.     adc    dx,word ptr linity+2    ; dx,ax = 2(X*Y)+B
  789.     jo    done0
  790.     mov    word ptr y,ax        ; save the new value of y
  791.     mov    word ptr y+2,dx     ;  ...
  792.     mov    ax,oldcolor        ; recall the old color
  793.     cmp    ax,k            ; check it against this iter
  794.     jle    short chkmaxit        ;  nope.  bypass periodicity check.
  795.     call    checkperiod        ; check for periodicity
  796.  
  797. chkmaxit:
  798.     cmp    show_orbit,0        ; orbiting on?
  799.     jne    horbit            ;  yep.
  800.     jmp    nextit            ;go around again
  801.  
  802. jmpdone0:
  803.     jmp    done0            ; DOES [(X*X)-(Y*Y)+P] BEFORE THE DEC.
  804.  
  805. horbit: push    bx            ; save my flags
  806.     mov    ax,-1            ; color for plot orbit
  807.     push    ax            ;  ...
  808.     push    word ptr y+2        ; co-ordinates for plot orbit
  809.     push    word ptr y        ;  ...
  810.     push    word ptr x+2        ;  ...
  811.     push    word ptr x        ;  ...
  812.     call    far ptr iplot_orbit    ; display the orbit
  813.     add    sp,5*2            ; clear out the parameters
  814.     pop    bx            ; restore flags
  815.     jmp    nextit            ; go around again
  816.  
  817. code32bit    endp
  818.  
  819.        end
  820.